﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Reflection;
using System.Web.Mvc;
using Core.Models;

namespace Core.Plugin
{
    public class PluginHelper
    {
        internal static void InitializePluginsAndLoadViewLocations()
        {
            try
            {
                CoreEntities core = new CoreEntities();
                string corename = Assembly.GetExecutingAssembly().GetName().Name;
                List<Core.Models.Plugin> clist = core.Plugins.Where(m => m.Name == corename).ToList();
                Core.Models.Plugin c;
                if (clist.Count <= 0)
                {
                    c = new Models.Plugin();
                    c.PluginId = new Guid(((System.Runtime.InteropServices.GuidAttribute)(Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(System.Runtime.InteropServices.GuidAttribute), false)[0])).Value);
                    c.Name = corename;
                    c.Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
                    c.DBVersionRequired = c.Version;
                    c.IsDroppedYN = "N";
                    core.AddToPlugins(c);
                }
                else
                {
                    c = core.Plugins.First(m => m.Name == corename);
                }
                if (new Version(c.Version).CompareTo(Assembly.GetExecutingAssembly().GetName().Version) != 0) c.Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
                Assembly[] pluginAssemblies = GetPluginAssemblies();
                List<string> viewLocations = new List<string>();
                foreach (Assembly plugin in pluginAssemblies)
                {
                    var pluginAttribute = plugin.GetCustomAttributes(typeof(PluginViewLocations), false).FirstOrDefault() as PluginViewLocations;

                    //Process the Plugin only if the Core version requirement is full filled
                    Version reqversion = new Version(pluginAttribute.CoreVersionRequired);
                    Version coreversion = Assembly.GetExecutingAssembly().GetName().Version;
                    if (coreversion.CompareTo(reqversion) >= 0)
                    {
                        if (pluginAttribute != null)
                            viewLocations.AddRange(pluginAttribute.ViewLocations);

                        // Register the Plugin in the Database
                        Core.Models.Plugin p;
                        List<Core.Models.Plugin> plugins = core.Plugins.Where(m => m.Name == pluginAttribute.Name).ToList();
                        if (plugins.Count > 0)
                        {
                            p = plugins[0];
                            p.DBVersionRequired = pluginAttribute.DBVersionRequired;
                            p.Area = pluginAttribute.Area;
                            p.Controller = pluginAttribute.Controller;
                            p.Action = pluginAttribute.Action;
                            p.UserPermission = pluginAttribute.UserPermission;
                            p.AdminController = pluginAttribute.AdminController;
                            p.AdminAction = pluginAttribute.AdminAction;
                            p.AdminPermission = pluginAttribute.AdminPermission;
                            p.IsIndexedYN = pluginAttribute.IsIndexedYN;
                        }
                        else
                        {
                            p = new Models.Plugin();
                            p.PluginId = new Guid(((System.Runtime.InteropServices.GuidAttribute)(plugin.GetCustomAttributes(typeof(System.Runtime.InteropServices.GuidAttribute), false)[0])).Value);
                            p.Name = pluginAttribute.Name;
                            p.IsDroppedYN = "N";
                            p.Version = plugin.GetName().Version.ToString();
                            p.DBVersionRequired = pluginAttribute.DBVersionRequired;
                            p.Area = pluginAttribute.Area;
                            p.Controller = pluginAttribute.Controller;
                            p.Action = pluginAttribute.Action;
                            p.UserPermission = pluginAttribute.UserPermission;
                            p.AdminController = pluginAttribute.AdminController;
                            p.AdminAction = pluginAttribute.AdminAction;
                            p.AdminPermission = pluginAttribute.AdminPermission;
                            p.IsIndexedYN = pluginAttribute.IsIndexedYN;
                            core.AddToPlugins(p);
                        }
                    }
                }
                core.SaveChanges();

                // Replace the WebFormViewEngine with our custom derived from it.
                // ViewEngines.Engines.Clear();
                int i;
                int fnd = -1;
                for (i = 0; i < ViewEngines.Engines.Count; i++)
                {
                    string engine = ViewEngines.Engines[i].ToString();
                    if (engine.Equals("System.Web.Mvc.RazorViewEngine")) fnd = i;
                }
                if (fnd > -1) ViewEngines.Engines.RemoveAt(fnd);
                ViewEngines.Engines.Add(new PluginViewEngine(viewLocations.ToArray()));
            }
            catch
            {
            }
        }

        /*
        public static List<PluginAction> GetPluginActions()
        {
            CoreEntities core = new CoreEntities();
            Assembly[] pluginAssemblies = GetPluginAssemblies();

            List<PluginAction> pluginLinks = new List<PluginAction>();

            foreach (Assembly plugin in pluginAssemblies)
            {
                var pluginAttribute = plugin.GetCustomAttributes(typeof(PluginViewLocations), false).FirstOrDefault() as PluginViewLocations;
                Core.Models.Plugin p;
                List<Core.Models.Plugin> plugins = core.Plugins.Where(m => m.Name == pluginAttribute.Name).ToList();
                if (plugins.Count > 0)
                {
                    p = plugins[0];
                    p.DBVersionRequired = pluginAttribute.DBVersionRequired;
                    p.Controller = pluginAttribute.Controller;
                    p.Action = pluginAttribute.Action;
                    p.AdminController = pluginAttribute.AdminController;
                    p.AdminAction = pluginAttribute.AdminAction;
                    p.AdminPermission = pluginAttribute.AdminPermission;
                }
                else
                {
                    p = new Models.Plugin();
                    p.PluginId = Guid.NewGuid();
                    p.Version = plugin.GetName().Version.ToString();
                    p.DBVersionRequired = pluginAttribute.DBVersionRequired;
                    p.Controller = pluginAttribute.Controller;
                    p.Action = pluginAttribute.Action;
                    p.AdminController = pluginAttribute.AdminController;
                    p.AdminAction = pluginAttribute.AdminAction;
                    p.AdminPermission = pluginAttribute.AdminPermission;
                    core.AddToPlugins(p);
                }

                pluginLinks.Add(new PluginAction()
                {
                    Name = pluginAttribute.Name,
                    DBVersionRequired = pluginAttribute.DBVersionRequired,
                    Action = pluginAttribute.Action,
                    Controller = pluginAttribute.Controller,
                    AdminController = pluginAttribute.AdminController,
                    AdminAction = pluginAttribute.AdminAction,
                    AdminPermission = pluginAttribute.AdminPermission
                });
            }
            core.SaveChanges();
            return pluginLinks;
        }
        

        public static void RegisterPlugins()
        {
            CoreEntities core = new CoreEntities();
            Assembly[] pluginAssemblies = GetPluginAssemblies();

            foreach (Assembly plugin in pluginAssemblies)
            {
                var pluginAttribute = plugin.GetCustomAttributes(typeof(PluginViewLocations), false).FirstOrDefault() as PluginViewLocations;
                Core.Models.Plugin p;
                List<Core.Models.Plugin> plugins = core.Plugins.Where(m => m.Name == pluginAttribute.Name).ToList();
                if (plugins.Count > 0)
                {
                    p = plugins[0];
                    p.DBVersionRequired = pluginAttribute.DBVersionRequired;
                    p.Controller = pluginAttribute.Controller;
                    p.Action = pluginAttribute.Action;
                    p.AdminController = pluginAttribute.AdminController;
                    p.AdminAction = pluginAttribute.AdminAction;
                    p.AdminPermission = pluginAttribute.AdminPermission;
                }
                else
                {
                    p = new Models.Plugin();
                    p.PluginId = Guid.NewGuid();
                    p.Name = pluginAttribute.Name;
                    p.IsDroppedYN = "N";
                    p.Version = plugin.GetName().Version.ToString();
                    p.DBVersionRequired = pluginAttribute.DBVersionRequired;
                    p.Controller = pluginAttribute.Controller;
                    p.Action = pluginAttribute.Action;
                    p.AdminController = pluginAttribute.AdminController;
                    p.AdminAction = pluginAttribute.AdminAction;
                    p.AdminPermission = pluginAttribute.AdminPermission;
                    core.AddToPlugins(p);
                }
            }
            core.SaveChanges();
            return;
        }
        */

        private static Assembly[] GetPluginAssemblies()
        {
            IEnumerable<Assembly> loadedPluginAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetCustomAttributes(typeof(PluginViewLocations), false).Count() > 0).AsEnumerable();
            Assembly[] distinctLoadedPluginAssemblies = loadedPluginAssemblies.Distinct(new DLLComparer()).ToArray();
            return distinctLoadedPluginAssemblies;
        }

        public class PluginAction
        {
            public string Name { get; set; }
            public string CoreVersionRequired { get; set; }
            public string DBVersionRequired { get; set; }
            public string Area { get; set; }
            public string Controller { get; set; }
            public string Action { get; set; }
            public string UserPermission { get; set; }
            public string AdminController { get; set; }
            public string AdminAction { get; set; }
            public string AdminPermission { get; set; }
            public string IsIndexedYN { get; set; }
        }

        private class DLLComparer : IEqualityComparer<Assembly>
        {
            #region IEqualityComparer<Assembly> Members

            public bool Equals(Assembly x, Assembly y)
            {
                return x.ManifestModule.ScopeName == y.ManifestModule.ScopeName;
            }

            public int GetHashCode(Assembly obj)
            {
                return obj.ManifestModule.ScopeName.GetHashCode();
            }

            #endregion
        }
    }
}